//
// Created by 17775 on 2024/6/24.
//
#include <thread>
#include <iostream>
#include <QtGui>
#include <cmath>
#include <QMutex>
#include <utility>
#include <vector>
#include "RayCiPro_C.h"
#include "../utils/TimeM.h"
#include "../utils/utils.h"
#include "../LiveMode/livemode_2d_view.h"
#include "../utils/RayCiDataReceiveQueueObserver.h"
#include "../utils/PowerProfileDataReceiveQueueObserver.h"
#include "../utils/LiveModeFloatData.h"

#ifndef RAYCI_RAYCI_H
#define RAYCI_RAYCI_H

using namespace std;

enum class MethodsType {
    None = 0,
    Threshold = 1,
    _2ndMoments = 2,
    FitGauss = 3,
    FitSuperGauss = 4,
    Plateau = 5,
    SimpleGeometry = 6,
    Area = 7,
    KnifeEdge = 8,
    MovingSlit = 9,
    Dropdown = 10
};

struct sCameraItem
{
    sCameraItem() : dwLowId(0), dwHighId(0) {}
    sCameraItem(QString name, QString serial, const uint32_t lowId, const uint32_t highId)
            : sName(std::move(name)), serialNum(std::move(serial)), dwLowId(lowId), dwHighId(highId) {}

    QString sName;
    QString serialNum;
    uint32_t dwLowId, dwHighId;
};

struct AOI {
    int nShape = 0;
    int nMethod = 0;
    bool bActive = false;
    double dPositionX_px = 0;
    double dPositionY_px = 0;
    double dMajor_px = 0;
    double dMinor_px = 0;
    double dAzimuthAngle_px = 0;
};

//typedef std::vector<sCameraItem> tVecCameras;

enum eDataFormat { FORMAT_UNKNOWN, FORMAT_UINT8, FORMAT_FLOAT, FORMAT_CNT };

class LiveMode: public QObject {

Q_OBJECT

signals:
    inline void RayCi_Closed();
    void newFrameNum(QString frameNum, QString sTime, int nSizeX, int nSizeY, QString colorFormat, int bitDepth);
    void newCamInfo(std::vector<sCameraItem> m_vecCameras);
    void newCamSetting(LiveMode *rayCi);
    void newCamSelectionInfo(QString accessory, double floatingAverage, int summing);
    void newCamExposureTimeList(std::vector<double>	_vecExposureTimes);
    void updateControl(bool isExposureTimeAutomatic);
    void pixelSizeInfo(double dPixelSizeX, double dPixelSizeY) const;
    void newFrame();
    void newOptionsExposurement(int index);
    // AOI椭圆实时更新信号
    void aoiParametersChanged(const AOI& aoiData);
    void newOptionsGainAndBinning(int GainIndex, int Binning);
    void InitBackGroundButton(bool canLoad, bool canSave, bool canClear);
    void NoCCDorDog(QString str);

public slots:
    void thresholdValueChanged(MethodsType methods, int index) const;
    void changeSigmaEnvironment(MethodsType methods, int index) const;
    void changeFraction(MethodsType methods, int index) const;
    void changeSlope(MethodsType method, bool slopeIsChecked) const;

    // LiveMode AOI
    void setAOIShape(int shape) const;

private:
    string _strIP;
    string _strPort;
    int  _nDocIdMeasure = 0;
    bool _bUseOpenLive = false;
//    bool _bCropToAoi = FALSE;
//    bool _bCropToAoiAvailable = false;
    DWORD _dwCamIdLow = 0, _dwCamIdHigh = 0;
    QImage image;
    QPixmap currentPixmap;
    std::shared_ptr<QPixmap> pixmap = nullptr;
    std::shared_ptr<vector<float>> floatVec = nullptr;
    QMutex imageMutex;
    QMutex lutMutex;


    // camera parameter
    std::vector<double>	_vecGain;
    QSize _sizeData;
    // eDataFormat _dataFormat;
    QMutex _csecImageData;

    // data
    int m_nFrameNrCallback = 0;
    QString m_nFrameNrRetrieve;

    // callback IDs
    int _nIdCallback_LiveModeNewFrame = 0;
    int _nIdCallback_LiveModeClosed = 0;
    int	_nIdCallback_LiveModeCameraRemoved = 0;

    int m_nIdCallback_RayCiClosed = 0;

//    tVecCameras m_vecCameras;
    std::vector<sCameraItem> m_vecCameras;

    // control worker by event
    std::condition_variable data_cond;

    std::condition_variable m_cvStartGetNewFrame;
    std::condition_variable m_cvCancelGetNewFrame;
    QMutex m_mutex;
    std::thread m_workerThread;
    std::thread m_powerProfileThread;
    std::thread m_queryThread;

    ManualReset _startGetNewFrame = ManualReset(false);
    LiveMode_2D_View *liveMode2DView = nullptr;

    enum DataFormat { FORMAT_UINT8, FORMAT_FLOAT };
    DataFormat m_dataFormat;
    QSize m_sizeData;
//    TimeM m_timeFps;
    TimeM m_timeDataQuery;
    TimeM m_timeDataConvert;
    bool m_bCropToAoi = false;
    bool m_bCropToAoiAvailable = false;
    int findExposureTimeIndex(const std::vector<double>& exposureTimes, double targetExposure);
    int findGainIndex(const std::vector<double>& gains, double targetGain);
    void QueryCameraList();
    void CloseLiveMode();
    void QueryCameraParameter();
    void queryParameter();
    QString OnUpdateSize() const;
    // QString OnUpdateFps();
    QString OnUpdateTimeQuery() const;
    QString OnUpdateTimeConvert() const;
    void OnBnClickedCheckCropToAoi();

    double dSigmaEnv = 0 , dThreshold = 0;
    bool dFraction = false;
    bool isEllipse = false, bEllipseAvailable = false, bFractionAvailable = false, bBeamCoordinates = false,bBeamCoordinatesAvailable = false, bMethodAvailable = false,
    bRectangle = false, bRectangleAvailable = false, bSigmaEnvAvailable = false, bSlope = false, bSlopeAvailable = false, bThresholdAvailable = false;

    std::atomic<bool> queryParameterThreadRunning = true;
//    void copyFullImageData(const std::vector<float> &rawData, const std::shared_ptr<CCDImageData<double>>& ImageData) const;

    // power profile
    bool powerProfileIsAvailable = false, powerProfileIsValid = false;
    int powerProfileDataLength = 0;
    bool bSaturationViewValid = false;
    std::atomic<bool> isAutoContrast = true;

public:
    LiveMode();
    ~LiveMode() override;
    void close(bool t);
    int dExposureTimeindex=-1;
    int nSizeX = 0, nSizeY = 0;
    int getXSize();
    int getYSize();
    int _nDocIdLive = 0;
    bool Connect();
    void DisConnect();
    void ConnectionToLiveMode();
    DWORD Worker_GetNewFrame();
    void updateAOIArea(double x, double y, double radiusX, double radiusY, double angle,int index) const;
    double getPositionX_px() const;
    double getPositionY_px() const;
    double getMajor_px() const;
    double getMinor_px() const;
    void InitImage(int cx, int cy);
    QImage _imgData;
    std::vector<float> vecData;
    std::vector<BYTE> vecPreview;
    std::vector<double>	_vecExposureTimes;
    std::atomic<bool> getNewPowerDataThreadRunning = false;
    std::atomic<bool> getNewFrameThreadRunning = false;
    QImage& getImage() {
        return image;
    }
    std::shared_ptr<QPixmap>& getQPixmap() {
        return pixmap;
    }
    std::shared_ptr<std::vector<float>>& getFloatData() {
        return floatVec;
    }
    std::mutex _mutex;
    std::shared_ptr<LiveModeFloatData<std::vector<float>>> floatData = nullptr;
    void setAutoContrast(bool isAuto);
    void setLUTAutoContrast(bool isAuto) const;

    void set2DMethods(MethodsType method);
    bool setSpatialUnit(const QString &unit) const;
    QString getSpatialUnit() const;
    void setSpatialUnitManually(bool bSpatialManually, double dSizeX, double dSizeY) const;
    void setPowerUnitManually(bool bPowerManually) const;
    void setDivergenceActive(bool bDivergenceActive) const;
    void startBackgroundCalibration() const;
    void stopBackgroundCalibration() const;
    void loadBackground() const;
    void saveBackground() const;
    void clearBackground() const;
    void getSaturationView();
    void setExposureTime(int index) const;
    void setGain(int index) const;
    void setBinning(int index) const;
    void setAutoExposureTime(bool autoExposure) const;
    void setAutoGain(bool autoGain) const;
    void setReducePixelClock(bool bReducePixelClock) const;
    double getExposureTime() const;
    int getGain() ;
    int getBinning() ;
    double getMaxFrameRate() const;
    double getMinFrameRate() const;
    double getFrameRate() const;
    void AdjustAoiToCenter(bool check);
    void queryControl();
    void queryAOIParameter(MethodsType method);
    void updateSettings();
    void isMethodAvailable(MethodsType method);
    void sigmaIsChecked(int sigmaIndex) const;
    void thresholdIsChecked(int thresholdIndex) const;
    void powerFractionIsChecked(int fractionIndex) const;
    void getPowerData();
    void getPowerProfileData();
    void stopNewPowerProfileThread();
    void addPowerProfileDataListener(PowerProfileDataReceiveListener *listener) const;
    void getActualLut(BYTE LUT[256][4] , int nBits) const;
    void getBaseLut(BYTE LUT[256][4]) const;
//    std::array<std::array<BYTE, 4>, 256> getActualLut(int nBits);
    void setLutThreshold(double dMin, double dMax) const;
    void setAutoContrastAoiMask(int nAoiMask) const;
    QString getLutPathName() const;
    double getPositionX() const;
    double getPositionY() const;
    int getHorizontalOri() const;
    int getVerticalOri() const;

    // LiveMode 2D_View Setting
    void setAOIActive(bool active) const;
    void setAOIAdjustmentMethod(int iMethod) const;
    bool getAutoStatus() const;

    // LiveMode AOI
    AOI getPositionSize() const;
    void setPosition_px(double dPosX, double dPosY) const;
    double getAzimuthAngle() const;
    void setAOISize(double dMajor, double dMinor, double dPhi) const;
    void setHorizontalReference(bool isXImageOrAOI, int index) const;
    void setHorizontalOrigin(bool isLeftOrTop, int index) const;
    double getUserDefinedPositionX() const;
    double getUserDefinedPositionY() const;
    void setCoordSystemMode(int iMode);
    void setUserDefined(double dxPos, double dyPos) const;

    static void	CallbackRayCi_onExit(IN void* pContext);
    static void CallbackRayCi_Document_onClose(IN void* pContext, IN int nIdDoc);
    static void CallbackRayCi_LiveMode_onNewFrame(IN void* pContext, IN int nIdDoc, IN int nFrameNr, IN const tm* tmFrameTime, IN int nFrameTime_ms);
    static void CallbackRayCi_LiveMode_onCameraRemoved(IN void* pContext, IN int nIdDoc, IN int nIdCamHigh, IN int nIdCamLow);


    void setMainAOI(int index);
    void showOrHideAOI(int index,bool show) const;

    char szPUnit[80], szUnit[80];
    double dPixelSizeX = 0, dPixelSizeY = 0 , dTemperature = 0 , dWaveLength = 0, dExposureTime = 0, dGain = 0 ,dGainIndex=-1,dFrameRate=0, dMaximum = 0, dMinimum = 0, dUnitPower = 0, dTimeSpan = 0;
    bool isExposureTimeAvailable = false, isExposureTimeAutomatic = false, isGainAvailable = false, isGainAutomatic = false, pixelClockIsReduced = false,
    pixelClockIsAvailable = false, bAttenuatorAvailable = false, bPowerManually = false, bisAllExposure = false, bisCurrentExposure = false;
    int nBinning = 0,prevBinning=0, nFrameSpan = 0;
    QString cameraName;

    BYTE LUT[256][4] = {
            0,0,131,255,
            0,0,135,255,
            0,0,139,255,
            0,0,143,255,
            0,0,147,255,
            0,0,151,255,
            0,0,155,255,
            0,0,159,255,
            0,0,163,255,
            0,0,167,255,
            0,0,171,255,
            0,0,175,255,
            0,0,179,255,
            0,0,183,255,
            0,0,187,255,
            0,0,191,255,
            0,0,195,255,
            0,0,199,255,
            0,0,203,255,
            0,0,207,255,
            0,0,211,255,
            0,0,215,255,
            0,0,219,255,
            0,0,223,255,
            0,0,227,255,
            0,0,231,255,
            0,0,235,255,
            0,0,239,255,
            0,0,243,255,
            0,0,247,255,
            0,0,251,255,
            0,0,255,255,
            0,4,255,255,
            0,8,255,255,
            0,12,255,255,
            0,16,255,255,
            0,20,255,255,
            0,24,255,255,
            0,28,255,255,
            0,32,255,255,
            0,36,255,255,
            0,40,255,255,
            0,44,255,255,
            0,48,255,255,
            0,52,255,255,
            0,56,255,255,
            0,60,255,255,
            0,64,255,255,
            0,68,255,255,
            0,72,255,255,
            0,76,255,255,
            0,80,255,255,
            0,84,255,255,
            0,88,255,255,
            0,92,255,255,
            0,96,255,255,
            0,100,255,255,
            0,104,255,255,
            0,108,255,255,
            0,112,255,255,
            0,116,255,255,
            0,120,255,255,
            0,124,255,255,
            0,128,255,255,
            0,131,255,255,
            0,135,255,255,
            0,139,255,255,
            0,143,255,255,
            0,147,255,255,
            0,151,255,255,
            0,155,255,255,
            0,159,255,255,
            0,163,255,255,
            0,167,255,255,
            0,171,255,255,
            0,175,255,255,
            0,179,255,255,
            0,183,255,255,
            0,187,255,255,
            0,191,255,255,
            0,195,255,255,
            0,199,255,255,
            0,203,255,255,
            0,207,255,255,
            0,211,255,255,
            0,215,255,255,
            0,219,255,255,
            0,223,255,255,
            0,227,255,255,
            0,231,255,255,
            0,235,255,255,
            0,239,255,255,
            0,243,255,255,
            0,247,255,255,
            0,251,255,255,
            0,255,255,255,
            4,255,255,255,
            8,255,251,255,
            12,255,247,255,
            16,255,243,255,
            20,255,239,255,
            24,255,235,255,
            28,255,231,255,
            32,255,227,255,
            36,255,223,255,
            40,255,219,255,
            44,255,215,255,
            48,255,211,255,
            52,255,207,255,
            56,255,203,255,
            60,255,199,255,
            64,255,195,255,
            68,255,191,255,
            72,255,187,255,
            76,255,183,255,
            80,255,179,255,
            84,255,175,255,
            88,255,171,255,
            92,255,167,255,
            96,255,163,255,
            100,255,159,255,
            104,255,155,255,
            108,255,151,255,
            112,255,147,255,
            116,255,143,255,
            120,255,139,255,
            124,255,135,255,
            128,255,131,255,
            131,255,128,255,
            135,255,124,255,
            139,255,120,255,
            143,255,116,255,
            147,255,112,255,
            151,255,108,255,
            155,255,104,255,
            159,255,100,255,
            163,255,96,255,
            167,255,92,255,
            171,255,88,255,
            175,255,84,255,
            179,255,80,255,
            183,255,76,255,
            187,255,72,255,
            191,255,68,255,
            195,255,64,255,
            199,255,60,255,
            203,255,56,255,
            207,255,52,255,
            211,255,48,255,
            215,255,44,255,
            219,255,40,255,
            223,255,36,255,
            227,255,32,255,
            231,255,28,255,
            235,255,24,255,
            239,255,20,255,
            243,255,16,255,
            247,255,12,255,
            251,255,8,255,
            255,255,4,255,
            255,255,0,255,
            255,251,0,255,
            255,247,0,255,
            255,243,0,255,
            255,239,0,255,
            255,235,0,255,
            255,231,0,255,
            255,227,0,255,
            255,223,0,255,
            255,219,0,255,
            255,215,0,255,
            255,211,0,255,
            255,207,0,255,
            255,203,0,255,
            255,199,0,255,
            255,195,0,255,
            255,191,0,255,
            255,187,0,255,
            255,183,0,255,
            255,179,0,255,
            255,175,0,255,
            255,171,0,255,
            255,167,0,255,
            255,163,0,255,
            255,159,0,255,
            255,155,0,255,
            255,151,0,255,
            255,147,0,255,
            255,143,0,255,
            255,139,0,255,
            255,135,0,255,
            255,131,0,255,
            255,128,0,255,
            255,124,0,255,
            255,120,0,255,
            255,116,0,255,
            255,112,0,255,
            255,108,0,255,
            255,104,0,255,
            255,100,0,255,
            255,96,0,255,
            255,92,0,255,
            255,88,0,255,
            255,84,0,255,
            255,80,0,255,
            255,76,0,255,
            255,72,0,255,
            255,68,0,255,
            255,64,0,255,
            255,60,0,255,
            255,56,0,255,
            255,52,0,255,
            255,48,0,255,
            255,44,0,255,
            255,40,0,255,
            255,36,0,255,
            255,32,0,255,
            255,28,0,255,
            255,24,0,255,
            255,20,0,255,
            255,16,0,255,
            255,12,0,255,
            255,8,0,255,
            255,4,0,255,
            255,0,0,255,
            251,0,0,255,
            247,0,0,255,
            243,0,0,255,
            239,0,0,255,
            235,0,0,255,
            231,0,0,255,
            227,0,0,255,
            223,0,0,255,
            219,0,0,255,
            215,0,0,255,
            211,0,0,255,
            207,0,0,255,
            203,0,0,255,
            199,0,0,255,
            195,0,0,255,
            191,0,0,255,
            187,0,0,255,
            183,0,0,255,
            179,0,0,255,
            175,0,0,255,
            171,0,0,255,
            167,0,0,255,
            163,0,0,255,
            159,0,0,255,
            155,0,0,255,
            151,0,0,255,
            147,0,0,255,
            143,0,0,255,
            139,0,0,255,
            135,0,0,255,
            131,0,0,255
    };

protected:
    static bool IsPortAvailable(WORD wPort);
    RayCiDataReceiveQueueObserver *rayCiDataReceiveQueueObserver = nullptr;
    PowerProfileDataReceiveQueueObserver *powerProfileDataReceiveQueueObserver = nullptr;
};


#endif //RAYCI_RAYCI_H
